package com.simba.server.webservice;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import com.simba.common.log.LogUtil;
import com.simba.server.ServerParam;
import com.simba.server.base.CommonUtil;
import com.simba.server.commands.impl.web.WebSortInfoCmd;
import com.simba.server.components.data.ResultInfo;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

import javax.net.ssl.SSLContext;

public class WebServiceManager
{
	private static class WebServiceManagerHolder
	{
		private static final WebServiceManager INSTANCE = new WebServiceManager();
	}

	private static final Logger logger = Logger.getLogger(WebServiceManager.class);
	private static final int REQUEST_CONNECT_TIMEOUT = 5000;
	private static final int REQUEST_READ_SOCKET_TIME = 5000;

	public static WebServiceManager getInstance()
	{
		return WebServiceManagerHolder.INSTANCE;
	}

	private CloseableHttpClient httpClient;

	private WebServiceManager()
	{

	}

	public ResultInfo doNothing(String remoteUrl, String remoteMethod, String content) throws IOException
	{
		return null;
	}

	public ResultInfo doHttpServiceByPut(String remoteUrl, String remoteMethod, String content) throws IOException
	{
		HttpPut httpPut = new HttpPut(remoteUrl + remoteMethod);
		httpPut.setHeader("Content-type", "application/json");
		httpPut.setHeader("Charset", "UTF-8");
		httpPut.setHeader("Accept", "application/json");
		httpPut.setHeader("Accept-Charset", "UTF-8");

		StringEntity contentEntity = new StringEntity(content, StandardCharsets.UTF_8);
		contentEntity.setContentEncoding("UTF-8");
		contentEntity.setContentType("application/json");
		httpPut.setEntity(contentEntity);

		return doHttpService(httpPut,remoteMethod, content);
	}

	public ResultInfo doHttpServiceByPatch(String remoteUrl, String remoteMethod, String content) throws IOException
	{
		HttpPatch httpPatch = new HttpPatch(remoteUrl + remoteMethod);
		httpPatch.setHeader("Content-type", "application/json");
		httpPatch.setHeader("Charset", "UTF-8");
		httpPatch.setHeader("Accept", "application/json");
		httpPatch.setHeader("Accept-Charset", "UTF-8");

		StringEntity contentEntity = new StringEntity(content, StandardCharsets.UTF_8);
		contentEntity.setContentEncoding("UTF-8");
		contentEntity.setContentType("application/json");
		httpPatch.setEntity(contentEntity);

		return doHttpService(httpPatch,remoteMethod, content);
	}

	public ResultInfo doHttpServiceByGet(String remoteUrl, String remoteMethod, String content) throws IOException{
		HttpGetWithEntity httpGet = new HttpGetWithEntity(remoteUrl + remoteMethod);
		StringEntity contentEntity = new StringEntity(content, StandardCharsets.UTF_8);
		contentEntity.setContentEncoding("UTF-8");
		contentEntity.setContentType("application/json");
		httpGet.setEntity(contentEntity);
		return doHttpService(httpGet, remoteMethod, content);
	}

	public ResultInfo doHttpServiceByPost(String remoteUrl, String remoteMethod, String content) throws IOException
	{
		HttpPost postRequest = new HttpPost(remoteUrl + remoteMethod);
		StringEntity contentEntity = new StringEntity(content, StandardCharsets.UTF_8);
		contentEntity.setContentEncoding("UTF-8");
		contentEntity.setContentType("application/json");
		postRequest.setEntity(contentEntity);

		return doHttpService(postRequest, remoteMethod, content);
	}

	private ResultInfo doHttpService(final HttpUriRequest request, String remoteMethod, String content) throws IOException{
		String responseEntity = null;
		try {
			if (this.httpClient == null){
				initHttpClient();
			}

			//request.addHeader("Authorization", "Bearer "+ ServerParam.TOKEN);

			CloseableHttpResponse response = this.httpClient.execute(request);
			int statusCode = response.getStatusLine().getStatusCode();

			HttpEntity entity = response.getEntity();
			responseEntity = EntityUtils.toString(entity,StandardCharsets.UTF_8);
			String info = "[" + remoteMethod + "] Request Content:[" + content
					+ "] Response Content:["+statusCode+"][" + responseEntity + "]";
			LogUtil.LogInfo(logger, info);
			return new ResultInfo(statusCode, responseEntity);
		}catch (Exception e){

			LogUtil.LogError(logger,
					"Catch Unknown Exception when request WebService , Request Content:[" + content
							+ "] Exception Message:" + e.getMessage());
			return null;
		}
	}

	public void init()
	{
		this.initHttpClient();
	}

	private void initHttpClient()
	{
		PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
		connectionManager.setMaxTotal(600);
		connectionManager.setDefaultMaxPerRoute(200);

		RequestConfig requestConfig = RequestConfig
				.custom()
				.setConnectTimeout(REQUEST_CONNECT_TIMEOUT)
				.setSocketTimeout(REQUEST_READ_SOCKET_TIME)
				.build();

		SSLConnectionSocketFactory factory = getSingleSSLConnection();

		if (factory == null){
			this.httpClient = HttpClients
					.custom()
					.setConnectionManager(connectionManager)
					.setDefaultRequestConfig(requestConfig)
					.build();
		}else {
			this.httpClient = HttpClients
					.custom()
					.setSSLSocketFactory(factory)
					.setConnectionManager(connectionManager)
					.setDefaultRequestConfig(requestConfig)
					.build();
		}
	}

	private static SSLConnectionSocketFactory getSingleSSLConnection() {
		try {
			SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (paramArrayOfX509Certificate, paramString) -> true).build();
			return new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
		}catch (Exception e){
			return null;
		}
	}

	public void reset()
	{
		this.httpClient = null;
	}
}
